package home.ybubnov.bugcrss;

import home.ybubnov.bugcrss.entity.Update;
import home.ybubnov.bugcrss.entity.WebPage;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.regex.Pattern;

import javax.mail.Message;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.transaction.Transaction;

import difflib.Delta;
import difflib.DiffUtils;
import difflib.Patch;

/**
 * The class will retrieve page, make diff, update diff record and save page.
 * 
 * @author Yury
 * 
 */
public class PageRetriever extends HttpServlet {

	private final static String PAGE_URL = "http://www.bugc.org/";
	private final static String single = "\\w{3} \\w{3} \\d{2}, \\d{4}";
	private final static String singleNoDate = "\\w{3} \\d{2}, \\d{4}";
	private final static String period = "\\w{3} \\d{2}-\\d{2}, \\d{4}";

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		this.doPost(req, resp);
	}

	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		EntityManager em = EMF.emf.createEntityManager();
		EntityTransaction tx = em.getTransaction();
		try{
			// get page
			URL url = new URL(PAGE_URL);
			BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream()));
			StringBuilder sb = new StringBuilder();
			while (reader.ready()) {
				sb.append(reader.readLine()).append("\n");
			}
			reader.close();

			// load save page
			
			WebPage page = null;
			List<WebPage> results = (List<WebPage>) em.createNamedQuery("WebPage.getWebPage").getResultList();
			if (results.size() > 0) {
				page = results.get(0);
			}
			if (page == null) {
				page = new WebPage();
			}

			// do diff
			StringBuilder diffString = new StringBuilder();
			List<String> newPage = Arrays.asList(sb.toString().trim().split("\n"));
			List<String> oldPage = Arrays.asList(page.getContentAsString().trim().split("\n"));
			Patch patch = DiffUtils.diff(oldPage, newPage);
			if (patch.getDeltas() != null) {
				for (Delta delta : patch.getDeltas()) {
					if (delta.getRevised() != null && delta.getRevised().getLines() != null
							&& delta.getRevised().getLines().size() > 0) {
						for (String line : (List<String>) delta.getRevised().getLines()) {
							if (toSkip(line)) {
								continue;
							}
							String date = getDate(newPage, delta.getRevised().getPosition());
							diffString.append(date).append("<br>").append(line).append("<br>");
						}
					}
				}
			}

			if (diffString.length() == 0) {
				return;
			}
			// save diff and page
			tx.begin();
			page.setContentAsString(sb.toString());
			page.addUpdate(new Date(), diffString.toString());
			if (page.getUpdates().size() > 10){
				Update toDelete = page.getUpdates().poll();
				em.remove(toDelete);
			}
			em.persist(page);
			senEmails(page.getAddreses(), diffString.toString());
			resp.getWriter().print(sb.toString());
			tx.commit();
		} catch (Exception e) {
			if (tx != null && tx.isActive()) {
				tx.rollback();
			}
			throw new ServletException(e);
		} finally {
			if (em.getTransaction().isActive() && !em.getTransaction().getRollbackOnly()){
				em.getTransaction().commit();
			}
			em.close();
		}
	}

	public String getDate(List<String> lines, int startPosition) {
		String resuilt = "";

		for (int i = startPosition - 1; i >= 0; i--) {
			String line = lines.get(i);
			if (Pattern.matches(single, line) || Pattern.matches(singleNoDate, line) || Pattern.matches(period, line)) {
				return line;
			}
		}

		return resuilt;
	}

	private static boolean toSkip(String line) {
		if (line.equals(""))
			return true;

		if (line.startsWith("<link href"))
			return true;

		if (line.startsWith("<font size=\"3\"> Bookmark</font>"))
			return true;

		if (line.startsWith("Updated "))
			return true;

		if (Pattern.matches(single, line) || Pattern.matches(singleNoDate, line) || Pattern.matches(period, line))
			return true;

		return false;
	}
	
	private void senEmails(Set<String> addresses, String body){
		if (addresses == null || addresses.size() == 0){
			return;
		}
		Properties props = new Properties();
        Session session = Session.getDefaultInstance(props, null);
        try {
            Message msg = new MimeMessage(session);
            msg.setFrom(new InternetAddress("ybubnov@gmail.com", "BUGC"));
            List<InternetAddress> tos = new ArrayList<InternetAddress>(addresses.size());
            for(String address : addresses){
            	tos.add(new InternetAddress(address));
            }
            msg.addRecipients(Message.RecipientType.TO,tos.toArray(new InternetAddress[0]));
            msg.setSubject("New Events from BUGC");
            body +="<br/><br/>To unsubscribe, send email to <a href=\"mailto:unsubscribe@bugcrss.appspotmail.com\">unsubscribe@bugcrss.appspotmail.com</a>";
            msg.setContent(body, "text/html; charset=UTF-8");
            Transport.send(msg);

        } catch (Exception e) {
            throw new RuntimeException(e);
        }
	}

}
